package com.mtsmda.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.WeakHashMap;

public class MapDemo {

	public static void main(String[] args) {
		hashtableDemo();
		System.out.println("\n ----- \n");
		hashMapDemo();
		System.out.println("\n ----- \n");
		linkedHashMapDemo();
		System.out.println("\n ----- \n");
		treeMapDemo();
		System.out.println("\n ----- \n");
		weakHashMapDemo();
	}

	/**
	 * <p>
	 * This class implements a hash table, which maps keys to values. Any
	 * non-null object can be used as a key or as a value. To successfully store
	 * and retrieve objects from a hashtable, the objects used as keys must
	 * implement the hashCode method and the equals method.
	 * </p>
	 * */
	private static void hashtableDemo() {
		Map<String, List<String>> hashtableMap = new Hashtable<>();
		// hashtableMap.put(null, null);
		List<String> russia = new ArrayList<String>();
		russia.add("Moskva");
		russia.add("Sankt-Peterburg");
		russia.add("Omsk");
		russia.add("Vladivostok");
		russia.add("Soci");
		hashtableMap.put(new String("Russia"), russia);

		List<String> moldova = new ArrayList<String>();
		moldova.add("Kishinev");
		moldova.add("Belicy");
		moldova.add("Komrat");
		moldova.add("Kagul");
		moldova.add("Oknica");
		hashtableMap.put(new String("Moldova"), moldova);

		List<String> ucraina = new ArrayList<String>();
		ucraina.add("Kiev");
		ucraina.add("Odessa");
		ucraina.add("Dnepropetrovsc");
		ucraina.add("Reni");
		ucraina.add("Izmail");
		hashtableMap.put(new String("Ucraina"), ucraina);

		List<String> romyniya = new ArrayList<String>();
		romyniya.add("Timishoara");
		romyniya.add("Yassay");
		romyniya.add("Bucureshty");
		hashtableMap.put(new String("Romyniya"), romyniya);

		List<String> germaniya = new ArrayList<String>();
		germaniya.add("Berlin");
		germaniya.add("Bavariya");
		germaniya.add("Dortmunt");
		germaniya.add("Stutgart");
		germaniya.add("Gamburg");
		hashtableMap.put(new String("Germaniya"), germaniya);

		System.out.println(hashtableMap.size());

		System.out.println();

		Set<Entry<String, List<String>>> entrySet = hashtableMap.entrySet();
		Iterator<Entry<String, List<String>>> iterator = entrySet.iterator();
		while (iterator.hasNext()) {
			Entry<String, List<String>> next = iterator.next();
			System.out.print(next.getKey() + " = " + next.getValue());
			System.out.println();
		}

		System.out.println();

		Set<String> keySet = hashtableMap.keySet();
		for (String country : keySet) {
			System.out.print(country + " : " + hashtableMap.get(country));
			System.out.println();
		}

		System.out.println();

		System.out.println(hashtableMap.values());

		System.out.println();

		Collection<List<String>> values = hashtableMap.values();
		for (List<String> s : values) {
			for (String value : s) {
				System.out.print(value + ", ");
			}
			System.out.println();
		}

	}

	/**
	 * <p>
	 * Hash table based implementation of the Map interface. This implementation
	 * provides all of the optional map operations, and permits null values and
	 * the null key. (The HashMap class is roughly equivalent to Hashtable,
	 * except that it is unsynchronized and permits nulls.) This class makes no
	 * guarantees as to the order of the map; in particular, it does not
	 * guarantee that the order will remain constant over time. This
	 * implementation provides constant-time performance for the basic
	 * operations (get and put), assuming the hash function disperses the
	 * elements properly among the buckets. Iteration over collection views
	 * requires time proportional to the "capacity" of the HashMap instance (the
	 * number of buckets) plus its size (the number of key-value mappings).
	 * Thus, it's very important not to set the initial capacity too high (or
	 * the load factor too low) if iteration performance is important.
	 * </p>
	 * */
	private static void hashMapDemo() {
		Map<String, Integer> hashMap = new HashMap<>();
		System.out.println(hashMap.put(null, null));
		System.out.println(hashMap.put(null, 15));
		System.out.println(hashMap.put("Ivan", 17));
		System.out.println(hashMap.put("Fedor", 29));
		System.out.println(hashMap.put("Fedor", 38));

		System.out.println("*****");

		for (String s : hashMap.keySet()) {
			System.out.println(s + " - " + hashMap.get(s));
		}
	}

	/**
	 * <p>
	 * Hash table and linked list implementation of the Map interface, with
	 * predictable iteration order. This implementation differs from HashMap in
	 * that it maintains a doubly-linked list running through all of its
	 * entries. This linked list defines the iteration ordering, which is
	 * normally the order in which keys were inserted into the map
	 * (insertion-order). Note that insertion order is not affected if a key is
	 * re-inserted into the map. (A key k is reinserted into a map m if m.put(k,
	 * v) is invoked when m.containsKey(k) would return true immediately prior
	 * to the invocation.)
	 * </p>
	 * */
	private static void linkedHashMapDemo() {
		Map<String, String> linkedHashMap = new LinkedHashMap<>();
		linkedHashMap.put(null, null);
		linkedHashMap.put("Barcelona", "Messi");
		linkedHashMap.put("Milan", "Kaka");
		linkedHashMap.put("Real Madrid", "Raul");
		linkedHashMap.put("Chelsea", "Lampard");

		for (String s : linkedHashMap.keySet()) {
			System.out.println(s + " - " + linkedHashMap.get(s));
		}
	}

	/**
	 * <p>
	 * A Red-Black tree based NavigableMap implementation. The map is sorted
	 * according to the natural ordering of its keys, or by a Comparator
	 * provided at map creation time, depending on which constructor is used.
	 * </p>
	 * */
	private static void treeMapDemo() {
		Map<String, Double> treeMap = new TreeMap<>();
		// treeMap.put(null, null);
		treeMap.put("Barcelona", 750.50);
		treeMap.put("Real Madrid", 710.50);
		treeMap.put("Mancester United", 550.50);
		treeMap.put("Milan", 250.80);
		treeMap.put("Spartak", 70.36);
		treeMap.put("Barcelona", 850.50);

		for (String s : treeMap.keySet()) {
			System.out.println(s + " - " + treeMap.get(s));
		}
	}

	/**
	 * <p>
	 * Hash table based implementation of the Map interface, with weak keys. An
	 * entry in a WeakHashMap will automatically be removed when its key is no
	 * longer in ordinary use. More precisely, the presence of a mapping for a
	 * given key will not prevent the key from being discarded by the garbage
	 * collector, that is, made finalizable, finalized, and then reclaimed. When
	 * a key has been discarded its entry is effectively removed from the map,
	 * so this class behaves somewhat differently from other Map
	 * implementations. Both null values and the null key are supported. This
	 * class has performance characteristics similar to those of the HashMap
	 * class, and has the same efficiency parameters of initial capacity and
	 * load factor.
	 * 
	 * Like most collection classes, this class is not synchronized. A
	 * synchronized WeakHashMap may be constructed using the
	 * Collections.synchronizedMap method.
	 * </p>
	 * */
	private static void weakHashMapDemo() {
		Map<String, String> weakHashMap = new WeakHashMap<>();
		weakHashMap.put(null, null);
		weakHashMap.put("PPP", "Petrov Petr Petrovici");
		weakHashMap.put("III", "Ivanov Ivan Ivanovici");
		weakHashMap.put("AAA", "Artemov Artem Artemovici");
		weakHashMap.put("SSS", "Sidorov Sidr Sidorovici");
		weakHashMap.put("PPP", "Petrov Petr Petrovici9");

		for (String s : weakHashMap.keySet()) {
			System.out.println(s + " - " + weakHashMap.get(s));
		}
	}

}